package p.ithorns.framework.common.beans;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.*;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.annotation.Order;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Nonnull;
import java.lang.annotation.Annotation;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * BeanImporterProcessor
 * 选择性注入处理器
 *
 * @author Ht.L
 * @date 2023-06-21 14:15
 * @since 1.0.0
 */
@SuppressWarnings("unchecked")
@Order(-1)
@Component
public class BeanImporterProcessor implements BeanDefinitionRegistryPostProcessor {

    /**
     * 扫描注解类
     */
    private final Class<? extends Annotation> scanClass = BeanImporter.class;

    /**
     * 扫描包路径
     */
    private final String scanBasePackage = "p.ithorns.sample";

    @Override
    public void postProcessBeanDefinitionRegistry(@Nonnull BeanDefinitionRegistry registry) throws BeansException {
    }

    @Override
    public void postProcessBeanFactory(@Nonnull ConfigurableListableBeanFactory listableBeanFactory) throws BeansException {
        DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) listableBeanFactory;
        ClassPathScanningCandidateComponentProvider beanScanner =
                new ClassPathScanningCandidateComponentProvider(false);
        AnnotationTypeFilter includeFilter = new AnnotationTypeFilter(scanClass);
        beanScanner.addIncludeFilter(includeFilter);
        Set<BeanDefinition> beanDefinitions = beanScanner.findCandidateComponents(scanBasePackage);
        for (BeanDefinition bd : beanDefinitions) {
            AnnotatedBeanDefinition annoBeanDef = (AnnotatedBeanDefinition) bd;
            AnnotationMetadata annotationMetadata = annoBeanDef.getMetadata();
            // Assert.isTrue(annotationMetadata.isInterface(), "@ServiceImporter can only be specified on an interface");
            Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(scanClass.getCanonicalName());

            // 获取需要注册的Bean
            Class<?>[] classes = (Class<?>[]) Objects.requireNonNull(attributes).get("inject");
            for (Class<?> clazz : classes) {
                BeanDefinition definition = makeBeanDefinition(clazz);
                beanFactory.registerBeanDefinition(clazz.getSimpleName(), definition);
            }
        }
    }

    /**
     * 构建BeanDefinition
     *
     * @param beanClass -bean类型
     * @return BeanDefinition
     */
    private BeanDefinition makeBeanDefinition(Class<?> beanClass) {
        String canonicalName = beanClass.getCanonicalName();
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(canonicalName);
        GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();
        definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_CONSTRUCTOR);
        return definition;
    }

    protected Set<String> getBasePackages(AnnotationMetadata metadata) {
        Map<String, Object> attributes = metadata.getAnnotationAttributes(scanClass.getCanonicalName());
        Set<String> basePackages = new HashSet<>();
        for (String pkg : (String[]) attributes.get("basePackages")) {
            if (StringUtils.hasText(pkg)) {
                basePackages.add(pkg);
            }
        }
        for (Class<?> clazz : (Class<?>[]) attributes.get("basePackageClasses")) {
            basePackages.add(ClassUtils.getPackageName(clazz));
        }

        if (basePackages.isEmpty()) {
            basePackages.add(
                    ClassUtils.getPackageName(metadata.getClassName()));
        }
        return basePackages;
    }

    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        AnnotationMetadata metadata = beanDefinition.getMetadata();
        return metadata.hasAnnotation(BeanImporter.class.getName());
    }

}